#pragma once

#include "doc/convert/attribute_convert/attribute_convertor.h"
#include "doc/convert/attribute_convert/encode_length.h"

namespace hawking {
namespace indexlib {

/*
    memory layout:
    | ArrayLengthType(element count) | T1 | T2 | T3 | ......
*/
template <typename T>
class MultiValueAttributeConvertor : public AttributeConvertor
{
public:
    MultiValueAttributeConvertor(util::ColumnType column_type) :
        AttributeConvertor(column_type) {}
    ~MultiValueAttributeConvertor() = default;

protected:
    size_t GetSize_(const util::FieldValue* field_value) const {
        switch (column_type_)
        {
        case util::COLUMN_INT8_LIST:
        case util::COLUMN_INT16_LIST:
        case util::COLUMN_INT32_LIST:
        case util::COLUMN_INT64_LIST:
            return field_value->int_values().values_size();
        case util::COLUMN_FLOAT_LIST:
        case util::COLUMN_DOUBLE_LIST:
            return field_value->double_values().values_size();
        default:
            break;
        }

        return 0;
    }

    void AppendStrLength_(StringLengthType str_length, char*& buffer) const {
        std::memcpy(buffer, &str_length, sizeof(str_length));
        buffer += sizeof(str_length);
    }

    void AppendCount_(ArrayLengthType array_size, char*& buffer) const {
        std::memcpy(buffer, &array_size, sizeof(array_size));
        buffer += sizeof(array_size);
    }

    void WriteElements_(const util::FieldValue* field_value, size_t array_size, char* buffer) const {
        switch (column_type_)
        {
        case util::COLUMN_INT8_LIST:
        case util::COLUMN_INT16_LIST:
        case util::COLUMN_INT32_LIST:
        case util::COLUMN_INT64_LIST:
            {
                T* buf = reinterpret_cast<T*>(buffer);
                for (size_t idx = 0; idx < array_size; ++idx) {
                    buf[idx] = static_cast<T>(field_value->int_values().values(idx));
                }
            }
            break;
        case util::COLUMN_FLOAT_LIST:
        case util::COLUMN_DOUBLE_LIST:
            {
                T* buf = reinterpret_cast<T*>(buffer);
                for (size_t idx = 0; idx < array_size; ++idx) {
                    buf[idx] = static_cast<T>(field_value->double_values().values(idx));
                }
            }
            break;
        default:
            break;
        }
    }

    // length + every element
    virtual std::string_view InnerEncode_(const util::FieldValue* field_value, Pool* pool) override {
        ArrayLengthType array_size = static_cast<ArrayLengthType>(GetSize_(field_value));
        char* begin = Allocate_(pool, sizeof(T) * array_size + sizeof(array_size));
        char* buffer = begin;
        AppendCount_(array_size, buffer);
        WriteElements_(field_value, array_size, buffer);
        return std::string_view(begin, sizeof(T) * array_size + sizeof(array_size));
    }
};

}
}